package com.bid;

import java.io.IOException;
import java.util.ArrayList;

import javax.microedition.khronos.opengles.GL10;

import org.anddev.andengine.audio.music.Music;
import org.anddev.andengine.audio.music.MusicFactory;
import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.BoundCamera;
import org.anddev.andengine.engine.handler.IUpdateHandler;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.IEntity;
import org.anddev.andengine.entity.modifier.MoveModifier;
import org.anddev.andengine.entity.particle.ParticleSystem;
import org.anddev.andengine.entity.particle.emitter.PointParticleEmitter;
import org.anddev.andengine.entity.particle.initializer.AccelerationInitializer;
import org.anddev.andengine.entity.particle.initializer.ColorInitializer;
import org.anddev.andengine.entity.particle.initializer.RotationInitializer;
import org.anddev.andengine.entity.particle.initializer.VelocityInitializer;
import org.anddev.andengine.entity.particle.modifier.AlphaModifier;
import org.anddev.andengine.entity.particle.modifier.ColorModifier;
import org.anddev.andengine.entity.particle.modifier.ExpireModifier;
import org.anddev.andengine.entity.particle.modifier.ScaleModifier;
import org.anddev.andengine.entity.primitive.Rectangle;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.scene.background.ColorBackground;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.entity.text.Text;
import org.anddev.andengine.entity.util.FPSLogger;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.input.touch.detector.ScrollDetector;
import org.anddev.andengine.input.touch.detector.ScrollDetector.IScrollDetectorListener;
import org.anddev.andengine.input.touch.detector.SurfaceScrollDetector;
import org.anddev.andengine.opengl.font.Font;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.ui.activity.BaseGameActivity;
import org.anddev.andengine.util.Debug;
import org.anddev.andengine.util.HorizontalAlign;
import org.anddev.andengine.util.modifier.IModifier;
import org.anddev.andengine.util.modifier.IModifier.IModifierListener;
import org.anddev.andengine.util.modifier.ease.EaseElasticInOut;
import org.anddev.andengine.util.modifier.ease.EaseElasticOut;
import org.anddev.andengine.util.modifier.ease.EaseLinear;
import org.anddev.andengine.util.modifier.ease.IEaseFunction;

import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.Typeface;

public class LevelPicker extends BaseGameActivity implements IScrollDetectorListener, IOnSceneTouchListener{
	// ===========================================================
	// Constants
	// ===========================================================
	private float SCENE_WIDTH;
	private float SCENE_HEIGHT;
	private float BLOCK_WIDTH;
	private float BLOCK_HEIGHT;
	
	private static final int SEASONS = 3;
	
	// ===========================================================
	// Fields
	// ===========================================================
	private BoundCamera mCamera;
	private Scene mScene;
	
	private BitmapTextureAtlas mSeasonBackgroundTextureAtlas;
	private BitmapTextureAtlas mSeasonTextureAtlas;
	private TextureRegion mSeasonBackgroundTextureRegion;
	private TextureRegion [] mSeasonsTextureRegion = new TextureRegion[SEASONS];
	private TiledTextureRegion mItemTextureRegion;
	private TextureRegion mParticleTextureRegion;
	private BitmapTextureAtlas mFontTexture;
	
	private Music mScratchSound;
	
	private Font mFont;
	private PointParticleEmitter particleEmitter;
	private SurfaceScrollDetector mScrollDetector;
	private boolean mTriggered = false;
	private Rectangle rectToFollow = new Rectangle(0, 0, 1f, 1f);
	
	private ArrayList <Season> mSeasons = new ArrayList();
	
	public static int UnlockedLevel;
	public static int unlockPending;
	public static int levelSelected;
	// ===========================================================
	// Constructors
	// ===========================================================
	
	// ===========================================================
	// Getter & Setter
	// ===========================================================
	private void initialize(){
		SCENE_WIDTH = 800 * SEASONS * BID.ratioMedium;
		SCENE_HEIGHT = 480 * 2f * BID.ratioMedium;
		BLOCK_WIDTH = SCENE_WIDTH / SEASONS;
		BLOCK_HEIGHT = SCENE_HEIGHT / 2f;
		
	}
	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================
	
	@Override
	public Engine onLoadEngine() {
		initialize();
		
		/*Load engine. */
        mCamera = new BoundCamera(0, 0, BID.CAMERA_WIDTH, BID.CAMERA_HEIGHT);
        mCamera.setCenter(BLOCK_WIDTH / 2f, BLOCK_HEIGHT * 1.5f);
        mCamera.setBoundsEnabled(true);
        mCamera.setBounds(-100, SCENE_WIDTH + 100, SCENE_HEIGHT / 2f, SCENE_HEIGHT);
        
        final EngineOptions engineOptions = new EngineOptions(true, ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(BID.CAMERA_WIDTH, BID.CAMERA_HEIGHT), mCamera);
        engineOptions.getTouchOptions().setRunOnUpdateThread(true);
        engineOptions.setNeedsMusic(true);
        
        return new Engine(engineOptions);
	}

	@Override
	public void onLoadResources() {
        this.mSeasonBackgroundTextureAtlas = new BitmapTextureAtlas(512, 512, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		this.mSeasonTextureAtlas = new BitmapTextureAtlas(512, 1024, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
		
        this.mSeasonBackgroundTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mSeasonBackgroundTextureAtlas, this, "season_background.png", 0, 0);//512x512
        this.mSeasonsTextureRegion[0] = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mSeasonTextureAtlas, this, "season_1.png", 0, 0);//512x256
        this.mSeasonsTextureRegion[1] = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mSeasonTextureAtlas, this, "season_2.png", 0, 256);//512x256 
        this.mSeasonsTextureRegion[2] = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mSeasonTextureAtlas, this, "season_3.png", 0, 512);//512x256
        this.mItemTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mSeasonTextureAtlas, this, "season_item.png", 0, 768, 2, 1);//128*2x128
        this.mParticleTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mSeasonTextureAtlas, this, "particle_spark.png", 256, 768);//32x32
        
        this.mFontTexture = new BitmapTextureAtlas(256, 256, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
		this.mFont = new Font(this.mFontTexture, Typeface.create(Typeface.DEFAULT, Typeface.BOLD), 64, true, Color.GREEN);
		
		this.mEngine.getTextureManager().loadTexture(this.mFontTexture);
		this.mEngine.getFontManager().loadFont(this.mFont);
        this.mEngine.getTextureManager().loadTexture(this.mSeasonBackgroundTextureAtlas);
        this.mEngine.getTextureManager().loadTexture(this.mSeasonTextureAtlas);
	}

	@Override
	public Scene onLoadScene() {
		this.mEngine.registerUpdateHandler(new FPSLogger());
		
		this.mScene = new Scene();
		this.mScene.setBackground(new ColorBackground(0f, 0f, 0f));
		this.mScene.setOnAreaTouchTraversalFrontToBack();
		this.mScene.setOnSceneTouchListener(this);
		
		this.mScrollDetector = new SurfaceScrollDetector(this);
		
		this.mScene.attachChild(this.rectToFollow);
		
		final SharedPreferences levelSettings = getSharedPreferences("level", MODE_PRIVATE);		
		this.UnlockedLevel = levelSettings.getInt("level_unlocked", 8);
		
		createSeasons();
		createSparks();
		unlockLevels();
		
		return this.mScene;
	}

	@Override
	public void onLoadComplete() {
	
	}
	
	@Override
	public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
		int down = 0;
		for(int i = 0; i < this.mSeasons.size(); i ++){
			if(equals(this.mSeasons.get(i).getY(), 0))
				down ++;
		}
		if(down == SEASONS){
			this.mScrollDetector.onTouchEvent(pSceneTouchEvent);
			this.rectToFollow.clearEntityModifiers();
			this.mCamera.setChaseEntity(null);
			
			if(pSceneTouchEvent.isActionUp()){
				for(int i = 0; i < this.mSeasons.size(); i ++){
					float xMin = this.mSeasons.get(i).getX();
					float xMax = this.mSeasons.get(i).getX() + BLOCK_WIDTH;
					float toX = this.mSeasons.get(i).getX() + BLOCK_WIDTH / 2f;
					if(equals(this.mCamera.getCenterX(), toX))
						break;
					if(xMin <= this.mCamera.getCenterX() && this.mCamera.getCenterX() <= xMax){
						IEaseFunction efSide = EaseLinear.getInstance();
						this.rectToFollow.setPosition(this.mCamera.getCenterX(), this.mCamera.getCenterY());
						this.rectToFollow.registerEntityModifier(new MoveModifier(1 * (Math.abs(this.rectToFollow.getX() - toX)) / BLOCK_WIDTH, this.rectToFollow.getX(), toX, this.mCamera.getCenterY(), this.mCamera.getCenterY(), efSide));
						
						this.mCamera.setChaseEntity(this.rectToFollow);
						break;
					}
				}
				
				this.particleEmitter.setCenter(-300, 0);
			}
			return true;
		}
	
		return false;
	}
	
	@Override
	public void onScroll(ScrollDetector pScollDetector, TouchEvent pTouchEvent, float pDistanceX, float pDistanceY) {
		this.mCamera.offsetCenter(-pDistanceX * 2f, 0);
		this.mTriggered = true;
		
		/*Particles*/
		if(pDistanceX >= 0)
			this.particleEmitter.setCenter(this.mCamera.getCenterX() - (float)Math.random() * BID.CAMERA_WIDTH / 2f, SCENE_HEIGHT);
		else
			this.particleEmitter.setCenter(this.mCamera.getCenterX() + (float)Math.random() * BID.CAMERA_WIDTH / 2f, SCENE_HEIGHT);
		
		/*Scratch music*/
		if(BID.soundOn == false)
			return ;
		float random = -0.03f + (float)Math.random() * 0.031f;
		if(this.mScratchSound.isPlaying() == false){
			this.mScratchSound.setVolume(0.1f);
			this.mScratchSound.play();
		}
		if(this.mScratchSound.getVolume() + random > 0.06f && this.mScratchSound.getVolume() + random < 0.3f)
			this.mScratchSound.setVolume(this.mScratchSound.getVolume() + random);
	}
	// ===========================================================
	// Methods
	// ===========================================================
	private boolean equals(final float x1, final float x2){
		return Math.abs(x1 - x2) <= 1;
	}
	
	private void createSeasons(){
		for(int i = 0; i < SEASONS; i ++){
			final Season season = new Season(i * BLOCK_WIDTH,0, BLOCK_WIDTH, BLOCK_HEIGHT * 2f,this.mSeasonBackgroundTextureRegion, i);
			this.mScene.attachChild(season);
			this.mSeasons.add(season);
		}
	}
	
	private void createSparks(){
		particleEmitter = new PointParticleEmitter(-300, 0);
		final ParticleSystem particleSystem = new ParticleSystem(particleEmitter, 70, 100, 200, this.mParticleTextureRegion);
		particleSystem.setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE);
		
		particleSystem.addParticleInitializer(new VelocityInitializer(-20 + (float)Math.random() * 21f, 5 + (float)Math.random() * 5f, -60, -100));
		particleSystem.addParticleInitializer(new AccelerationInitializer(-10 + (float)Math.random() * 11f, (float)Math.random() * 20f));
		particleSystem.addParticleInitializer(new RotationInitializer(0.0f, 30.0f));
		particleSystem.addParticleInitializer(new ColorInitializer(0.6f, 0.0f, 0.1f));

		particleSystem.addParticleModifier(new ScaleModifier(0.2f, 0.15f, 0.2f, 0.3f, 0f, 1f));
		particleSystem.addParticleModifier(new ExpireModifier(1f));
		particleSystem.addParticleModifier(new AlphaModifier(1.0f, 0.0f, 0.5f, 1f));
		particleSystem.addParticleModifier(new ColorModifier(0.6f, 0.8f, 0.0f, 0.2f, 0.2f, 0.3f, 0.5f, 1f));

		this.mScene.attachChild(particleSystem);
		
		/*Music*/
		MusicFactory.setAssetBasePath("mfx/");
		try {
			this.mScratchSound = MusicFactory.createMusicFromAsset(this.mEngine.getMusicManager(), this, "scratch_sound.ogg");
		} catch (final IOException e) {
			Debug.e(e);
		}
		this.mScratchSound.setVolume(0.15f);
	}
	
	public void unlockLevel(final int level){
		final int seasonNumber = level / 9;
		final int itemNumber = level % 9;
		
		final Season.Item item = this.mSeasons.get(seasonNumber).items.get(itemNumber);
		item.setCurrentTileIndex(1);
	}
	
	private void unlockLevels(){
		for(int i = 0; i <= this.UnlockedLevel; i ++)
			unlockLevel(i);
		this.mScene.registerUpdateHandler(new IUpdateHandler(){
			@Override
			public void onUpdate(float pSecondsElapsed) {
				if(unlockPending > UnlockedLevel){
					final SharedPreferences levelSettings = getSharedPreferences("level", MODE_PRIVATE);
					final SharedPreferences.Editor editor = levelSettings.edit();
					LevelPicker.UnlockedLevel ++;
					editor.putInt("level_unlocked", LevelPicker.UnlockedLevel);
					unlockLevel(LevelPicker.UnlockedLevel);
				}
			}
			@Override
			public void reset() {
			}
		});
	}
	
	private void startGameCore(){
		Intent intent = new Intent(LevelPicker.this,GameCore.class);
		startActivity(intent);
	}
	
	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================

	private class Season extends Sprite{
		private float x = BLOCK_WIDTH / 8f;
		private float y = BLOCK_HEIGHT / 10f;
		private float width = BLOCK_WIDTH - 2 * x;
		private float height = BLOCK_HEIGHT - 2 * y;
		
		private float spaceX = 15;
		private float spaceY = 15f;
		private float itemWidth = (width - 4 * spaceX) / 3f;
		private float itemHeight = (height - 4 * spaceY) / 3f;
		
		private float sceneX, sceneY;
		
		private int number;
		private ArrayList <Item> items = new ArrayList();
		
		public Season(float pX, float pY, float pWidth, float pHeight, TextureRegion pTextureRegion, int number) {
			super(pX, pY, pWidth, pHeight, pTextureRegion);
			sceneX = pX;
			sceneY = pY;
			this.number = number;
			
			buildSeason(number);
		}
		
		/*Methods*/
		public void buildSeason(int number){
			/*Upper Side */
			float yy = y;
			for(int i = 0; i < 3; i ++){
				float xx = x;
				yy += spaceY;
				for(int j = 0;j < 3; j ++){
					xx += spaceX;
					final Item item = new Item(xx, yy, itemWidth, itemHeight, LevelPicker.this.mItemTextureRegion.clone(), i * 3 + j, this.number);
					LevelPicker.this.mScene.registerTouchArea(item);
					this.items.add(item);
					this.attachChild(item);
					xx += itemWidth;
				}
				yy += itemHeight;
			}
			
			/*Lower Side */
			Sprite lower = new Sprite(x, BLOCK_HEIGHT + y, width, height, LevelPicker.this.mSeasonsTextureRegion[number]){
				private boolean isDown = false;
				@Override
				public boolean contains(final float x, final float y){
				 if(!super.contains(x, y)){
					 isDown = false;
					 this.setScale(1f);
	                 return false;
	             }
	             return true;
				}
				@Override
				public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
					if(pSceneTouchEvent.isActionDown() && LevelPicker.this.equals(LevelPicker.this.mCamera.getCenterX(), sceneX + LevelPicker.this.BLOCK_WIDTH / 2f)){
						isDown = true;
						this.setScale(1.05f);
						LevelPicker.this.mTriggered = false;
						return false;
					}
					if(pSceneTouchEvent.isActionMove() && LevelPicker.this.mTriggered){
						isDown = false;
						this.setScale(1f);
						return false;
					}
					if(pSceneTouchEvent.isActionUp()){
						if(isDown && !LevelPicker.this.mTriggered){
							slideDown();
						}
						isDown = false;
						this.setScale(1f);
						return false;
					}
					return false;
				}
			};
			this.attachChild(lower);
			LevelPicker.this.mScene.registerTouchArea(lower);
		}
		private void slideDown(){
			IEaseFunction efDown = EaseElasticOut.getInstance();
			this.clearEntityModifiers();
			this.registerEntityModifier(new MoveModifier(3, sceneX, sceneX, sceneY, sceneY + BLOCK_HEIGHT, efDown));
		}
		
		private void slideUp(){
			IEaseFunction efUp = EaseElasticInOut.getInstance();
			this.clearEntityModifiers();
			this.registerEntityModifier(new MoveModifier(3, sceneX, sceneX, sceneY + BLOCK_HEIGHT, sceneY, efUp));
		}
		
		/*Inner Class*/
		private class Item extends AnimatedSprite{
			private int number, season;
			private boolean isDown = false;
			private Text textCenter;
			public Item(float pX, float pY, float pWidth, float pHeight, TiledTextureRegion pTextureRegion, int number, int season) {
				super(pX, pY, pWidth, pHeight, pTextureRegion);
				this.number = number;
				this.season = season;
				drawNumber(number);
			}
			
			private void drawNumber(int number){
				this.textCenter = new Text(this.getWidth() / 2f - 32, this.getHeight() / 2f - 32, LevelPicker.this.mFont, number + 1 + "", HorizontalAlign.CENTER);
				this.attachChild(this.textCenter);
			}
			
			@Override
			public boolean contains(final float x, final float y){
			 if(!super.contains(x, y)){
                 isDown = false;
                 this.setScale(1f);
                 return false;
             }
			 this.setScale(1.1f);
             return true;
			}
			
			@Override
			public boolean onAreaTouched(final TouchEvent pSceneTouchEvent, final float pTouchAreaLocalX, final float pTouchAreaLocalY) {
				if(pSceneTouchEvent.isActionDown()){
					isDown = true;
					this.setScale(1.1f);
					return true;
				}
				if(pSceneTouchEvent.isActionUp()){
					if(isDown){
						if(this.getCurrentTileIndex() == 1){
							startGameCore();
							LevelPicker.levelSelected = season * 9 + number;
						}
						slideUp();
					}
					isDown = false;
					this.setScale(1f);
					return true;
				}
				return false;
			}
		}
	}
	
}
